package com.etcfg.etlaunch;

import java.awt.AWTException;
import java.awt.CheckboxMenuItem;
import java.awt.Menu;
import java.awt.MenuItem;
import java.awt.PopupMenu;
import java.awt.SystemTray;
import java.awt.TrayIcon;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.ItemEvent;
import java.awt.event.ItemListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.net.BindException;
import java.net.SocketException;
import java.net.URL;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

import javax.swing.ImageIcon;
import javax.swing.SwingUtilities;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;

import com.etcfg.etlaunch.ApplicationSetting.SlacMode;
import com.etcfg.etlaunch.ui.MainWindow;
import com.etcfg.etlaunch.ui.SettingsDialog;

public class ETLauncherApplication {
	private File serverListFile;
	private ServerStatusChecker checker;
	// private ThreadPoolExecutor executor;
	private ExecutorService executorService = Executors.newFixedThreadPool(4);
	private ApplicationSetting applicationSetting;
	private ETLauncher etLauncher;
	private MainWindow mainWindow;

	private Map<String, ETServer> servers = new HashMap<String, ETServer>();
	private boolean run = true;
	private boolean noRefresh = false;
	private Map<ETServer, ServerStatusChecker> statusCheckers = new HashMap<ETServer, ServerStatusChecker>();

	public ETLauncherApplication() {
		this.setServerListFile(serverListFile);
		serverListFile = new File(System.getProperty("user.home") //$NON-NLS-1$
				+ "\\etcfgLaunch\\" + "serverList.txt"); //$NON-NLS-1$ //$NON-NLS-2$
		serverListFile.getParentFile().mkdirs();

		applicationSetting = new ApplicationSetting();
		applicationSetting.load();
		// executor = new ThreadPoolExecutor(4, 10, 10, TimeUnit.SECONDS,
		// new ArrayBlockingQueue<Runnable>(40));

		etLauncher = new ETLauncher(applicationSetting);

		try {
			checker = new ServerStatusChecker();
		} catch (SocketException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}

	public void setEtLauncher(ETLauncher etLauncher) {
		this.etLauncher = etLauncher;
	}

	public ETLauncher getEtLauncher() {
		return etLauncher;
	}

	public void setApplicationSetting(ApplicationSetting applicationSetting) {
		this.applicationSetting = applicationSetting;
	}

	public ApplicationSetting getApplicationSetting() {
		return applicationSetting;
	}

	public ExecutorService getExecutor() {
		return executorService;
	}

	public void setChecker(ServerStatusChecker checker) {
		this.checker = checker;
	}

	public ServerStatusChecker getChecker() {
		return checker;
	}

	public void setServerListFile(File serverListFile) {
		this.serverListFile = serverListFile;
	}

	public File getServerListFile() {
		return serverListFile;
	}

	void loadServerList() {
		synchronized (servers) {

			if (getServerListFile().exists()) {
				FileInputStream fileInputStream = null;
				DataInputStream dis = null;

				try {
					try {

						fileInputStream = new FileInputStream(
								getServerListFile());
						dis = new DataInputStream(fileInputStream);
						while (dis.available() > 0) {
							String serverAdress = dis.readUTF();
							int port = dis.readInt();
							String serverName = dis.readUTF();

							ETServer etServer = new ETServer();
							etServer.setIp(serverAdress);
							etServer.setPort(port);
							etServer.setServerName(serverName);
							if (applicationSetting.getLoadedVersion() != null) {

								if (dis.readBoolean()) {
									etServer.setPassword(dis.readUTF());
								}

							}
							servers.put(etServer.getIp() + ":" //$NON-NLS-1$
									+ etServer.getPort(), etServer);

						}

					} finally {
						if (dis != null) {
							dis.close();
						}

						if (fileInputStream != null) {
							fileInputStream.close();
						}
					}
				} catch (FileNotFoundException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
		}
	}

	public void saveServerList() {
		synchronized (servers) {
			if (!getServerListFile().exists()) {
				try {
					getServerListFile().createNewFile();
				} catch (IOException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
			}
			FileOutputStream fileOutputStream = null;
			DataOutputStream dos = null;
			try {
				try {
					fileOutputStream = new FileOutputStream(getServerListFile());
					dos = new DataOutputStream(fileOutputStream);
					for (ETServer etServer : servers.values()) {
						dos.writeUTF(etServer.getIp());
						dos.writeInt(etServer.getPort());
						dos.writeUTF(etServer.getServerName());
						if (etServer.getPassword() != null
								&& etServer.getPassword().isEmpty() == false) {
							dos.writeBoolean(true);
							dos.writeUTF(etServer.getPassword());
						} else {
							dos.writeBoolean(false);
						}
					}
				} finally {
					if (fileOutputStream != null) {
						fileOutputStream.close();
					}
					if (dos != null) {
						dos.close();
					}
				}
			} catch (FileNotFoundException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			} catch (IOException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
	}

	public ETServer addServer(ETServer etServer) {
		String key = etServer.getIp() + ":" + etServer.getPort(); //$NON-NLS-1$
		System.out.println("Adding server"); //$NON-NLS-1$
		synchronized (servers) {
			System.out
					.println("Lock for adding server sequired, adding server"); //$NON-NLS-1$

			if (servers.containsKey(key) == false) {
				servers.put(key, etServer);
				mainWindow.getListModel().addServer(etServer);
				saveServerList();
				return etServer;
			}
			return servers.get(key);
		}
	}

	public void init() {
		loadServerList();
		executorService.execute(new ServerRefresh());
	}

	public static void main(String[] args) {
		String property = System.getProperty("user.home"); //$NON-NLS-1$
		System.out.println("User dir" + property); //$NON-NLS-1$
		try {
			// Set System L&F
			UIManager
					.setLookAndFeel("com.sun.java.swing.plaf.windows.WindowsLookAndFeel"); //$NON-NLS-1$
		} catch (UnsupportedLookAndFeelException e) {
			// handle exception
		} catch (ClassNotFoundException e) {
			// handle exception
		} catch (InstantiationException e) {
			// handle exception
		} catch (IllegalAccessException e) {
			// handle exception
		}
		final ETLauncherApplication application = new ETLauncherApplication();

		if (args != null && args.length > 0) {
			final ApplicationSetting applicationSetting2 = application
					.getApplicationSetting();

			try {
				SwingUtilities.invokeAndWait(new Runnable() {

					@Override
					public void run() {

						while (applicationSetting2.getPathToEtExe() == null
								|| applicationSetting2.getPathToSlac() == null) {
							SettingsDialog dialog = new SettingsDialog(
									applicationSetting2, null);
							dialog.setAlwaysOnTop(true);
							dialog.centerOnScreen();
							dialog.setTitle(Messages
									.getString("ETLauncherApplication.10")); //$NON-NLS-1$
							dialog.setVisible(true);

							if (!dialog.isSettingsSet()) {
								System.exit(1);
							}
						}
						applicationSetting2.save();

					}
				});
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			} catch (InvocationTargetException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}

			silentStart(args, application);
		} else {
			application.init();
			application.start();
		}

	}

	private static void silentStart(String[] args,
			ETLauncherApplication application) {
		application.getEtLauncher().setWaitUntilFinished(false);
		String adress = null;
		String password = null;
		Map<String, String> addParams = new HashMap<String, String>();

		// +set fs_game etpro +password letmehurtyou +connect
		// 94.75.248.74:27960
		for (int i = 0; i < args.length; i++) {
			String par = args[i];
			if ("+set".equals(par)) { //$NON-NLS-1$
				String parName = args[++i];

				addParams.put(parName, args[++i]);

				continue;
			}
			if ("+connect".equals(par)) { //$NON-NLS-1$
				adress = args[++i];
				continue;
			}
			if ("+password".equals(par)) { //$NON-NLS-1$
				password = args[++i];
			}
		}
		if (adress != null) {
			ETServer etServer = new ETServer();
			etServer.setAdditionalParametes(addParams);
			String[] split = adress.split(":"); //$NON-NLS-1$
			etServer.setIp(split[0]);
			etServer.setPort(Integer.parseInt(split[1]));
			if (password != null) {
				etServer.setPassword(password);
			}
			ServerStatus status;
			try {
				application.noRefresh = true;

				status = application.getChecker().checkStatus(etServer.getIp(),
						etServer.getPort(), false);
				etServer.setServerStatus(status);

				application.getEtLauncher().startGameBlocking(etServer);
				System.exit(1);
			} catch (Exception e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}

		}
	}

	private void start() {

		try {
			RequestHandler handler = RequestHandler.getHandler(this);
			Thread thread = new Thread(handler);
			thread.start();
		} catch (BindException e1) {
			try {
				URL url = new URL("http://127.0.0.1:" + RequestHandler.PORT
						+ "/" + RequestHandler.Requests.SHOW_WINDOW);
				url.getContent();

			} catch (Exception e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		} catch (Exception e1) {
			// TODO Auto-generated catch block
			e1.printStackTrace();

		} finally {
			if (RequestHandler.isHandlerInitialized() == false) {
				System.exit(1);
			}
		}

		mainWindow = new MainWindow(this);
		mainWindow.getFrame().setLocationByPlatform(true);
		mainWindow.getFrame().setVisible(true);
		mainWindow.getFrame().addWindowListener(new WindowAdapter() {

			@Override
			public void windowClosed(WindowEvent e) {
				// run = false;
			}
		});
		SystemTray tray = SystemTray.getSystemTray();
		initTrayIcon(tray);

		SwingUtilities.invokeLater(new Runnable() {

			@Override
			public void run() {
				for (ETServer etServer : servers.values()) {
					mainWindow.getListModel().addServer(etServer);
				}

				if (applicationSetting.isFirstStart()) {

				}

				while (applicationSetting.getPathToEtExe() == null
						|| applicationSetting.getPathToSlac() == null) {
					SettingsDialog dialog = new SettingsDialog(
							applicationSetting, mainWindow.getFrame());
					dialog.setAlwaysOnTop(true);
					dialog
							.setTitle(Messages
									.getString("ETLauncherApplication.setSettingsMessage")); //$NON-NLS-1$
					dialog.setLocationByPlatform(true);
					dialog.setLocationRelativeTo(mainWindow.getFrame());
					dialog.setVisible(true);
					if (!dialog.isSettingsSet()) {
						System.exit(1);
					}
				}
				if (applicationSetting.isCheckForUpdatesAtStart()) {
					System.out.println("Checking for updates");
					UpdateChecker checker = new UpdateChecker();
					checker.setSilent(true);
					checker.setParent(mainWindow.getFrame());
					executorService.execute(checker);
				}

			}
		});

	}

	private void initTrayIcon(final SystemTray tray) {
		final TrayIcon icon = new TrayIcon(new ImageIcon(getClass()
				.getResource("/com/etcfg/etlaunch/ui/icons/etcfgIcon-16.gif")) //$NON-NLS-1$
				.getImage());

		PopupMenu popupMenu = new PopupMenu();

		MenuItem connectToIpItem = new MenuItem(Messages
				.getString("ETLauncherApplication.connectToIp")); //$NON-NLS-1$
		final Menu connectToServerMenu = new Menu(Messages
				.getString("ETLauncherApplication.connectToServer")); //$NON-NLS-1$
		MenuItem addServer = new MenuItem(Messages
				.getString("ETLauncherApplication.addNewServer")); //$NON-NLS-1$
		MenuItem settingsItem = new MenuItem(Messages
				.getString("ETLauncherApplication.settingsMenuTitle")); //$NON-NLS-1$
		Menu slacMenu = new Menu(Messages
				.getString("ETLauncherApplication.slacMenuTitle")); //$NON-NLS-1$
		MenuItem exitItem = new MenuItem(Messages
				.getString("ETLauncherApplication.exit")); //$NON-NLS-1$

		final CheckboxMenuItem slacAutoItem = new CheckboxMenuItem(Messages
				.getString("ETLauncherApplication.slacAuto"),
				applicationSetting.getSlacMode().equals(SlacMode.AUTOMATIC));
		final CheckboxMenuItem slacOnItem = new CheckboxMenuItem(Messages
				.getString("ETLauncherApplication.slacOn"), applicationSetting
				.getSlacMode().equals(SlacMode.ALWAYS_ON));
		final CheckboxMenuItem slacOffItem = new CheckboxMenuItem(Messages
				.getString("ETLauncherApplication.slacOff"), applicationSetting
				.getSlacMode().equals(SlacMode.ALWAYS_OFF));

		slacMenu.add(slacAutoItem);
		slacMenu.add(slacOnItem);
		slacMenu.add(slacOffItem);

		popupMenu.add(connectToIpItem);
		popupMenu.add(connectToServerMenu);
		popupMenu.add(addServer);
		popupMenu.addSeparator();
		popupMenu.add(slacMenu);
		popupMenu.add(settingsItem);
		popupMenu.addSeparator();

		popupMenu.add(exitItem);

		icon.setPopupMenu(popupMenu);

		connectToIpItem.addActionListener(new ActionListener() {
			@Override
			public void actionPerformed(ActionEvent e) {
				ETServer showInsertServerIpDialog = mainWindow
						.showInsertServerIpDialog();
				if (showInsertServerIpDialog == null) {
					return;
				}
				mainWindow.getConnectToServerAction().setEtServer(
						showInsertServerIpDialog);
				mainWindow.getConnectToServerAction().actionPerformed(e);
			}
		});

		addServer.addActionListener(new ActionListener() {
			@Override
			public void actionPerformed(ActionEvent e) {
				mainWindow.getAddServerAction().actionPerformed(e);
			}
		});

		settingsItem.addActionListener(new ActionListener() {

			@Override
			public void actionPerformed(ActionEvent e) {
				mainWindow.getSettingsAction().actionPerformed(e);

			}
		});

		exitItem.addActionListener(new ActionListener() {
			@Override
			public void actionPerformed(ActionEvent e) {
				tray.remove(icon);
				System.exit(1);
			}
		});

		slacAutoItem.addItemListener(new ItemListener() {

			@Override
			public void itemStateChanged(ItemEvent e) {

				if (slacAutoItem.getState()) {
					applicationSetting.setSlacMode(SlacMode.AUTOMATIC);
					applicationSetting.save();
					slacAutoItem.setState(true);
					slacOffItem.setState(false);
					slacOnItem.setState(false);
				}

			}
		});

		slacOnItem.addItemListener(new ItemListener() {

			@Override
			public void itemStateChanged(ItemEvent e) {
				if (slacOnItem.getState()) {
					applicationSetting.setSlacMode(SlacMode.ALWAYS_ON);
					applicationSetting.save();
					slacOnItem.setState(true);
					slacAutoItem.setState(false);
					slacOffItem.setState(false);
				}

			}
		});

		slacOffItem.addItemListener(new ItemListener() {

			@Override
			public void itemStateChanged(ItemEvent e) {
				if (slacOffItem.getState()) {
					applicationSetting.setSlacMode(SlacMode.ALWAYS_OFF);
					applicationSetting.save();
					slacOffItem.setState(true);
					slacAutoItem.setState(false);
					slacOnItem.setState(false);
				}

			}
		}

		);

		icon.addMouseListener(new MouseAdapter() {
			@Override
			public void mouseReleased(MouseEvent e) {
				if (e.isPopupTrigger()) {
					connectToServerMenu.removeAll();
					synchronized (servers) {
						Collection<ETServer> values = servers.values();
						for (final ETServer etServer : values) {
							MenuItem item = new MenuItem(ColorConvertor
									.convertToPlainString(etServer
											.getServerName()));
							item.addActionListener(new ActionListener() {
								@Override
								public void actionPerformed(ActionEvent e) {
									mainWindow.getConnectToServerAction()
											.setEtServer(etServer);
									mainWindow.getConnectToServerAction()
											.actionPerformed(e);
									mainWindow.getConnectToServerAction()
											.setEtServer(null);

								}
							});
							connectToServerMenu.add(item);
						}
					}

				}
			}

			@Override
			public void mouseClicked(MouseEvent e) {
				int clickCount = e.getClickCount();
				if (clickCount == 2) {
					mainWindow.getFrame().setVisible(
							!mainWindow.getFrame().isVisible());
				}
			}
		});

		try {
			tray.add(icon);
		} catch (AWTException e1) {
			// TODO Auto-generated catch block
			e1.printStackTrace();
		}
		System.out.println(Arrays.toString(SystemTray.getSystemTray()
				.getTrayIcons()));
	}

	private class ServerRefresh implements Runnable {
		private long timeout = 1000;

		@Override
		public void run() {
			while (run) {
				if (etLauncher.isGameRunning()) {
					try {
						Thread.sleep(5000);
					} catch (InterruptedException e) {
						// TODO Auto-generated catch block
						e.printStackTrace();
					}
					continue;
				}
				if (noRefresh) {
					break;
				}
				Object[] keySet = null;
				synchronized (servers) {
					keySet = servers.keySet().toArray();
				}
				for (Object string : keySet) {
					ETServer etServer = null;
					synchronized (servers) {
						etServer = servers.get(string);
					}
					if (etServer == null) {
						continue;
					}
					try {
						ServerStatusChecker serverStatusChecker = null;

						serverStatusChecker = statusCheckers.get(etServer);
						if (serverStatusChecker == null) {
							serverStatusChecker = new ServerStatusChecker();
							statusCheckers.put(etServer, serverStatusChecker);
						}
						ServerStatus checkStatus = serverStatusChecker
								.checkStatus(etServer.getIp(), etServer
										.getPort(), false);

						etServer.setServerStatus(checkStatus);
					} catch (Exception e) {
						ServerStatus serverStatus = etServer.getServerStatus();
						if (serverStatus != null) {
							serverStatus.setPing(999);
						}

					}

				}
				try {
					Thread.sleep(timeout);
				} catch (InterruptedException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
			}

		}

	}

	public MainWindow getMainWindow() {
		return mainWindow;
	}

	public void deleteServer(ETServer etServer) {
		synchronized (servers) {
			String key = etServer.getIp() + ":" + etServer.getPort(); //$NON-NLS-1$
			servers.remove(key);
			saveServerList();
			statusCheckers.remove(etServer);
		}
	}

}